#include <sys/select.h>

#include "php.h"
#include "select.h"
#include "silent.h"

extern int silent_running;

typedef struct _select_fd {
	int fd;
	int type;
} select_fd_t;

typedef struct _select_reactor {
	int max_fd;
	fd_set rfds;
	zend_llist fds;
} select_reactor_t;

int slt_select_reactor_add(reactor_t *main_reactor, int fd, int type);
void slt_select_reactor_wait(reactor_t *main_reactor);
void slt_select_reactor_set(reactor_t *main_reactor, int type, handler_func_t func);


static void slt_select_apply_with_func(void *data, void *arg TSRMLS_DC) {
	select_fd_t *fd_node = data;
	select_reactor_t *object = arg;
	
	FD_SET(fd_node->fd, &(object->rfds));
}

void slt_select_reactor_free(reactor_t *main_reactor) {
	select_reactor_t *object = main_reactor->object;

	zend_llist_destroy(&(object->fds));
	efree(object);
}

void slt_select_reactor_create(reactor_t *main_reactor) {
	select_reactor_t *object = emalloc(sizeof(select_reactor_t));
	
	object->max_fd = 0;
	zend_llist_init(&(object->fds), sizeof(select_fd_t), NULL, 0);
	main_reactor->object = object;
	bzero(main_reactor->handlers, sizeof(main_reactor->handlers));
	main_reactor->add = slt_select_reactor_add;
	main_reactor->wait = slt_select_reactor_wait;
	main_reactor->set = slt_select_reactor_set;
	main_reactor->free = slt_select_reactor_free;
}

inline void slt_select_reactor_set(reactor_t *main_reactor, int type, handler_func_t func) {
	main_reactor->handlers[type] = func;
} 

int slt_select_reactor_add(reactor_t *main_reactor, int fd, int type) {
	select_fd_t *fd_node = emalloc(sizeof(select_fd_t));
	select_reactor_t *object = main_reactor->object;

	if (fd > FD_SETSIZE) {
		return -1;
	}

	fd_node->fd = fd;
	fd_node->type = type;

	if (object->max_fd < fd) {
		object->max_fd = fd;
	}
	zend_llist_add_element(&(object->fds), fd_node);	
	efree(fd_node);
}

static void slt_select_apply_with_emit_func(void *data, void *arg TSRMLS_DC) {
	select_fd_t *fd_node = data;
	reactor_t *main_reactor = arg;
	select_reactor_t *object = main_reactor->object;
	event_emit_node_t emit_node;
	
	if (FD_ISSET(fd_node->fd, &(object->rfds))) {
		emit_node.fd = fd_node->fd;
		emit_node.type = fd_node->type;
		emit_node.from_id = main_reactor->id;
		main_reactor->handlers[fd_node->type](main_reactor, &emit_node);
	}
}

void slt_select_reactor_wait(reactor_t *main_reactor) {
	select_reactor_t *object = main_reactor->object;
	struct timeval tv;
	int ret = -1;

	TSRMLS_FETCH_FROM_CTX(slt_ctx);

	while (silent_running > 0) {
		FD_ZERO(&(object->rfds));
		zend_llist_apply_with_argument(&(object->fds), slt_select_apply_with_func, object TSRMLS_CC);
		tv.tv_sec = 5;
                tv.tv_usec = 0;
		ret = select(object->max_fd + 1, &(object->rfds), NULL, NULL, &tv);
		if (ret == -1) {
			if (errno == EINTR) {
				continue ;
			}
			break;
		} else if (ret == 0) {
			continue ;
		} else {
			zend_llist_apply_with_argument(&(object->fds), slt_select_apply_with_emit_func, main_reactor TSRMLS_CC);
		}
	}
}
